package com.CmJava.context;

import com.CmJava.SpringInterface.AopBeanPostProcessor;
import com.CmJava.SpringInterface.BeanPostProcessor;
import com.CmJava.annotation.*;
import com.CmJava.annotation.aop.*;
import com.CmJava.bean.BeanDefinition;
import com.CmJava.service.MyService;
import com.CmJava.service.MyServiceImpl;


import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {
    private static Class config;
    private static List<Class> aopClassList;
    private static List<String> aopPointCutList;
    private static List<Class> classList;
    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
    private static Map<String, Object> singletonObjects = new ConcurrentHashMap();


    public ApplicationContext(Class config) {
        this.config = config;
        classList = new ArrayList<Class>();
        aopClassList = new ArrayList<Class>();
        aopPointCutList = new ArrayList<String>();


        //不存在@Configuration抛错
        if (!config.isAnnotationPresent(Configuration.class)) {
            throw new RuntimeException("Annotation '@Configuration' is not present");
        }

        //是否存在包扫描注解
        if (config.isAnnotationPresent(ComponentScan.class)) {
            //扫描bean
            ComponentScan componentScan = (ComponentScan) config.getAnnotation(ComponentScan.class);
            String path = componentScan.value().replace(".", "/");
            //获取文件夹，遍历,生成Class对象
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL url = classLoader.getResource(path);
            File file = new File(url.getFile());
            if (file.isDirectory()) {
                //递归遍历文件结构
                File[] files = file.listFiles();
                if (files.length > 0) {
                    treeFile(files, path, classLoader);
                }
            }

            // 获取BeanDefinition
            creatBeanDefinition(classList, aopClassList, beanDefinitionMap);

            //反射创建非懒加载实例
            instanceNotLazySingletonBean(beanDefinitionMap);


        } else {
            System.out.println("未检测到包扫描注解");
        }
    }

    private void creatBeanDefinition(List<Class> classList, List<Class> aopClassList, Map<String, BeanDefinition> beanDefinitionMap) {
        String beanName = "";
        Class beanClass = null;
        String scope = "";
        BeanDefinition beanDefinition = null;
        boolean isLazy = false;

        //生成BeanDefinition
        for (Class aClass : classList) {
            if (aClass.isAnnotationPresent(Lazy.class)) {
                isLazy = true;
            } else {
                isLazy = false;
            }
            if (aClass.isAnnotationPresent(Scope.class)) {
                scope = ((Scope) aClass.getAnnotation(Scope.class)).value();
            } else {
                scope = "singleton";
            }
            if (((Component) aClass.getAnnotation(Component.class)).value().equals("")) {
                beanName = aClass.getName();
            } else {
                beanName = ((Component) aClass.getAnnotation(Component.class)).value();
            }

            beanDefinition = new BeanDefinition(beanName, aClass.getName(), aClass, scope, isLazy);
            beanDefinitionMap.put(aClass.getName(), beanDefinition);
        }


        //获取切入点，注入到BeanDefinition
        String access = "";
        String pointCut = "";
        String classString = "";
        String pointCutMethod = "";
        String[] classSplit = null;
        Class beanClassAop = null;
        for (Class aClass : aopClassList) {
            for (Method method : aClass.getDeclaredMethods()) {
                if (method.isAnnotationPresent(Pointcut.class)) {
                    pointCut = method.getAnnotation(Pointcut.class).value();
                    if (pointCutMatch(pointCut)) {
                        pointCut = pointCut.substring(10, pointCut.indexOf("))") + 1);
                        access = pointCut.split(" ")[0];
                        classString = pointCut.split(" ")[1];
                        classSplit = classString.substring(0, classString.lastIndexOf("(..)")).split("\\.");
                        //切点方法
                        pointCutMethod = classSplit[classSplit.length - 1];
                        //切点类全限定名
                        classString = classString.substring(0, classString.indexOf("." + pointCutMethod));


                        if (beanDefinitionMap.containsKey(classString)) {
                            beanClassAop = beanDefinitionMap.get(classString).getBeanClass();
                            if (pointCutMethod.equals("*")) {
                                for (Method method1 : beanClassAop.getDeclaredMethods()) {
                                    beanDefinitionMap.get(classString).getPointCut().put(method1, aClass);
                                    break;
                                }
                            } else {
                                for (Method method1 : beanClassAop.getDeclaredMethods()) {
                                    if (method1.getName().equals(pointCutMethod)) {
                                        beanDefinitionMap.get(classString).getPointCut().put(method1, aClass);
                                        break;
                                    }
                                }
                            }

                        }
                    }
                    break;
                }
            }
        }


    }

    /**
     * 判断切点表达式是否有误。
     * 仅仅判断了是否execution(权限名 全限定名.方法名(..))这种形式
     *
     * @param pointCut
     * @return
     */
    private boolean pointCutMatch(String pointCut) {
        //execution(* com.bird.service.impl.PersonServiceBean.*(..))
        try {

            boolean matches = pointCut.matches("execution(.*)");
            if (!matches) {
                return matches;
            }
            pointCut = pointCut.substring(10, pointCut.indexOf("))") + 1);

            String access = pointCut.split(" ")[0];
            if (!(access == "*" || access == "public" || access == "protected" || access == "default" || access != "private")) {
                return false;
            }

            String classString = pointCut.split(" ")[1];
            if ((classString.indexOf("(..)") + 3) != classString.length() - 1) {
                return false;
            }


            return matches;
        } catch (Exception e) {
            return false;
        }
    }

    private void treeFile(File[] files, String path, ClassLoader classLoader) {
        if (files.length == 0) {
            return;
        }
        String filePath = "";
        String classPath = "";
        Class<?> aClass = null;
        for (File file : files) {
            if (file.isDirectory()) {
                if (file.listFiles().length > 0) {
                    treeFile(file.listFiles(), path, classLoader);
                }
            } else {
                try {
                    filePath = file.toString().replace("\\", "/");
                    classPath = filePath.substring(filePath.indexOf(path), filePath.indexOf(".class")).replace("/", ".");
                    aClass = classLoader.loadClass(classPath);
                    //获取切面
                    if (aClass.isAnnotationPresent(Aspect.class)) {
                        String pointCut = "";
                        boolean flag = true;
                        List<String> notPointCut = new ArrayList<>();
                        for (Method method : aClass.getDeclaredMethods()) {
                            if (method.isAnnotationPresent(Pointcut.class)) {
                                pointCut = method.getName();
                            } else {
                                String value = methodGetAopAnnotationValue(method);
                                value = value.substring(0, value.indexOf("("));
                                notPointCut.add(value);
                            }
                        }
                        //判断通知和切点的对应关系是否有误
                        for (String notPointCutName : notPointCut) {
                            if (!notPointCutName.equals(pointCut)) {
                                flag = false;
                                break;
                            }
                        }
                        if (flag) {
                            aopClassList.add(aClass);
                        }
                    }

                    //获取框架管理的的class
                    if (aClass.isAnnotationPresent(Component.class)) {
                        classList.add(aClass);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    private String methodGetAopAnnotationValue(Method method) {
        String value = "";
        if (method.isAnnotationPresent(After.class)) {
            value = method.getAnnotation(After.class).value();
        } else if (method.isAnnotationPresent(AfterReturning.class)) {
            value = method.getAnnotation(AfterReturning.class).value();
        } else if (method.isAnnotationPresent(AfterThrowing.class)) {
            value = method.getAnnotation(AfterThrowing.class).value();
        } else if (method.isAnnotationPresent(Around.class)) {
            value = method.getAnnotation(Around.class).value();
        } else if (method.isAnnotationPresent(Before.class)) {
            value = method.getAnnotation(Before.class).value();
        }
        if (value.equals("")) {
            throw new RuntimeException("获取通知方法注解的切点失败");
        } else {
            return value;
        }
    }


    private void instanceNotLazySingletonBean(Map<String, BeanDefinition> beanDefinitionMap) {
        BeanDefinition beanDefinition = null;
        Object bean = null;
        String beanName = "";
        for (String beanClassName : beanDefinitionMap.keySet()) {

            beanDefinition = beanDefinitionMap.get(beanClassName);
            if (beanDefinition.getScope().equals("singleton")) {

                if (!singletonExistByClassName(beanClassName)) {

                    bean = doCreateBean(beanClassName);

                    if (!beanDefinition.getBeanName().equals(beanClassName)) {
                        //有bean别名
                        singletonObjects.put(beanDefinition.getBeanName(), bean);
                    } else {
                        //无bean别名
                        singletonObjects.put(beanClassName, bean);
                    }
                }
            }
        }


    }

    /**
     * 创建bean实例
     *
     * @param beanClassName bean的类全限定名
     * @return
     */
    private Object doCreateBean(String beanClassName) {

        Object bean = null;

        if (beanDefinitionMap.containsKey(beanClassName)) {
            //存在bean定义就创建
            Class beanClass = beanDefinitionMap.get(beanClassName).getBeanClass();
            try {
                //实例化
                bean = beanClass.newInstance();
                //属性填充
                Object fieldObject = null;
                String fieldClassName = "";
                for (Field field : beanClass.getDeclaredFields()) {

                    if (field.isAnnotationPresent(Autowired.class)) {
                        fieldClassName = field.getType().getName();
                        //Autowired注入
                        if (beanDefinitionMap.get(fieldClassName).getPointCut().size()>0){
                            throw new RuntimeException("Autowired未考虑代理对象的注入");
                        }


                        //是否给框架管理
                        if (beanDefinitionMap.containsKey(fieldClassName) || childBeanDefinitionExist(fieldClassName) != null) {

                            if (singletonExistByClassName(fieldClassName)) {
                                //原生对象
                                fieldObject = getSingletonByClassName(fieldClassName);
                            } else {
                                //无对象
                                fieldObject = doCreateBean(fieldClassName);
                                singletonObjects.put(field.getName(), fieldObject);
                            }
                        }else {
                            fieldObject = doCreateBean(fieldClassName);
                        }

                    } else {
                        fieldObject = field.getType().newInstance();
                    }
                    field.setAccessible(true);
                    try {
                        field.set(bean, fieldObject);
                    }catch (Exception e){
                        System.out.println("代理对象不支持Autowire注入");
                        field.set(bean, field.getType().newInstance());
                    }
                }


                //AOP
                if (bean instanceof AopBeanPostProcessor) {
                    //BeanPostProcessor,初始化之前
                    bean = ((AopBeanPostProcessor) bean).postProcessBeforeInitialization(bean, beanClassName);

                    //一些其他的初始化逻辑，就不写了
                    //...

                    //BeanPostProcessor,初始化之后,AOP

                    bean = ((AopBeanPostProcessor) bean).postProcessAfterInitialization(bean, beanClassName, beanDefinitionMap.get(beanClassName).getPointCut());

                }

            } catch (InstantiationException e) {
                e.printStackTrace();
                System.out.println("请确认是否存在有参构造覆盖无参构造的情况");
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                System.out.println("请确认无参构造是否私有");

            }
        } else if (childBeanDefinitionExist(beanClassName) != null) {
            //存在bean子类定义则创建
            BeanDefinition beanDefinition = childBeanDefinitionExist(beanClassName);
            String className = beanDefinition.getBeanClassName();
            if (singletonExistByClassName(className)) {
                bean = getSingletonByClassName(className);
            } else {
                bean = doCreateBean(className);
            }
        } else {
            //框架不管理该bean
            Class<?> aClass = null;
            try {
                aClass = this.getClass().getClassLoader().loadClass(beanClassName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            bean = doCreateBeanNotBySpring(aClass);
        }

        if (bean != null) {
            return bean;
        } else {
            throw new RuntimeException("实例化bean失败");
        }
    }

    /**
     * Autowired非框架管理的bean
     *
     * @param aClass
     * @return
     */
    private Object doCreateBeanNotBySpring(Class<?> aClass) {

        //未完成
        Object bean = null;
        try {
            bean = aClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return bean;

    }

    /**
     * 查找是否存在参数类的子类BeanDefinition
     *
     * @param beanClassName
     * @return
     */
    private BeanDefinition childBeanDefinitionExist(String beanClassName) {
        Class<?> aClass = null;
        Class<?> beanClass = null;
        for (String className : beanDefinitionMap.keySet()) {
            try {
                aClass = this.getClass().getClassLoader().loadClass(beanClassName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            beanClass = beanDefinitionMap.get(className).getBeanClass();
            if (aClass.isAssignableFrom(beanClass)) {
                return beanDefinitionMap.get(className);
            }

        }
        return null;
    }

    /**
     * 根据全限定名，查找单例池是否存在该类单例
     *
     * @param beanClassName
     * @return
     */
    private Object getSingletonByClassName(String beanClassName) {
        Object bean = null;
        for (String beanName : singletonObjects.keySet()) {
            if (singletonObjects.get(beanName).getClass().getName().equals(beanClassName)) {
                bean = singletonObjects.get(beanName);
                break;
            }
        }
        return bean;
    }

    /**
     * 单例池是否存在给类对象
     *
     * @param beanClassName
     * @return
     */
    private boolean singletonExistByClassName(String beanClassName) {

        for (String beanName : singletonObjects.keySet()) {
            if (singletonObjects.get(beanName).getClass().getName().equals(beanClassName)) {
                return true;
            }
        }
        return false;
    }

    public Object getBean(String beanClassName) {

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanClassName);
        String beanName = beanDefinition.getBeanName();
        Object bean = null;

        if (beanDefinition.getPointCut().size() > 0) {
            //是个代理对象
            if (beanDefinition.getScope().equals("singleton")) {
                //获取单例对象
                if (singletonExistByProxyClassName(beanClassName)) {
                    //非懒加载
                    bean = singletonObjects.get(beanName);
                } else {
                    //懒加载
                    bean = doCreateBean(beanName);
                    singletonObjects.put(beanName, bean);
                }

            } else if (beanDefinition.getScope().equals("prototype")) {
                //获取原型对象
                bean = doCreateBean(beanName);
            }
        } else {
            //不是代理对象
            if (beanDefinition.getScope().equals("singleton")) {
                //获取单例对象
                if (singletonExistByClassName(beanClassName)) {
                    //非懒加载
                    bean = singletonObjects.get(beanName);
                } else {
                    //懒加载
                    bean = doCreateBean(beanName);
                    singletonObjects.put(beanName, bean);
                }

            } else if (beanDefinition.getScope().equals("prototype")) {
                //获取原型对象
                bean = doCreateBean(beanName);
            }

        }
        return bean;

    }

    private boolean singletonExistByProxyClassName(String beanClassName) {
        Class<?> aClass = null;
        try {
            aClass = this.getClass().getClassLoader().loadClass(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Class<?>[] interfaces = aClass.getInterfaces();


        for (String beanName : singletonObjects.keySet()) {
            if (singletonObjects.get(beanName) instanceof Proxy) {
                if (interfaces.length > 0) {
                    boolean flag = true;
                    for (Class<?> anInterface : interfaces) {
                        if (anInterface.isAssignableFrom(singletonObjects.get(beanName).getClass())) {
                            continue;
                        } else {
                            flag = false;
                            break;
                        }
                    }
                    if (flag) {
                        return true;
                    } else {
                        continue;
                    }
                }
            }
        }
        return false;
    }
}
